This section is summarizes the differences between ALM 2.0 and its predecessors; if you have not worked with ALM before, you should skip to the Summary section.
ALM 2.0 is enabled on desktops!
You asked via email. You asked over the phone. You asked at WWDC and other conferences. You posted hacks around the restriction on comp.sys.mac.portables. We heard. ALM 2.0 runs happily on desktops or PowerBooks (1.0.x only ran on PowerBooks), and is scheduled to be available to everyone as of an upcoming system release. We thank you for your support!
ALM 2.0 is easier to use!
In ALM 1.0.x, the user was faced with a window in which locations were listed, and a separate window in which settings were added to locations by clicking on a button to move them from the left to the right (MacWeek described this as somewhat too much like Font/DA mover, for those who remember System 6). In 2.0, there is one window, and settings are "on" or "off" via a checkbox, rather than moving from one list to another; this does mean that, if order is important, the user will have to move settings around, but it turns out that it almost never is important.
In addition, the EditSetting call, which was not used extensively in ALM 1.0.x, is fully-implemented in ALM 2.0, and module developers are strongly encouraged to support it so as to facilitate editing non-active settings for non-current locations.
Specific Details
Enough with the hype. A lot of the changes are generic stability-enhancements and bug fixes that I won't bore you with. Here are some of the details:
New API Calls
There are now calls for creating locations, merging settings into existing locations, and selecting locations, all with a normalized API. See ALMPutLocation, ALMMergeLocation, and ALMGetLocation for details. (the bit on gestaltALMAttr is gestaltALMHasSFLocation)
New File Types
ALM 1.0.x modules all have a file type of 'thng'. These files will continue to work with ALM 2.0, however, we now distinguish between "action" modules (which have a file type of 'almb') and "state" modules (which have a file type of 'almn'). Action modules did exist under ALM 1.0.x (the Auto-Open Item module was one), but the means for specifying an action module were never documented. Now they are. In addition, a future version of the Finder will be able to automatically route 'almb' and 'almn' files to the modules folder, whereas 'thng' module files will never be routed correctly (there are other 'thng' files that want to go to other places).
Additional Events
Since there have always been API calls that enable developers to build their own list of locations, ALM 2.0 send notifications to registered clients when the locations list has been changed (a location has been deleted, renamed, or created by the user). (the bit on gestaltALMAttr is gestaltALMHasRescanNotifiers)
Switch Escalation
A module can now decide what it's going to do based on what modules before it have done.
The Apple Location Manager (ALM), version 1.0, was announced at MacWorld Expo in San Francisco on January 6, 1997 (see link at bottom for full text):
...
Apple Computer, Inc. today announced a free software utility for PowerBook computers called Apple Location Manager. Apple Location Manager software makes it easy for PowerBook users to move from one location to another without having to spend a lot of time manually reconfiguring their computer with every move.
"Mobile users, by definition, change location frequently. Apple Location Manager takes the difficulty out of moving from one place to another because with a single command, a user can reconfigure his or her system to the settings needed at any given location," said Dale Fuller, vice president and general manager for the PowerBook division. "It's a small piece of software, but it does a big job and saves people a lot of time and inconvenience. It's what Apple is all about...making complex tasks simple."
...
http://product.info.apple.com/pr/press.releases/1997/q2/970106.pr.rel.locmgr.html
As you may gather from the above excerpt, ALM provides a user interface for saving and restoring sets of settings so that PowerBook users can reconfigure their systems easily as they move from place to place. As of ALM 2.0, desktop users can maintain their systems in similar fashion (whether they carry them around or not).
Although ALM ships with support for switching the default printer, TCP/IP settings, time zone, and a few other things, the ALM software is primarily an infrastructure for storing, retrieving, and applying sets of settings, and we expect third-party developers who are interested in meeting the needs of mobile users will be eager to use this framework to enhance their products, so we are providing this SDK.
Supporting ALM is not difficult - most of the time, you will not need to modify your product at all to add ALM support.
For most developers, "supporting" ALM means simply that - providing ALM with enough information to switch settings under user control in a way that is compatible with software that uses those settings. In addition to providing the infrastructure for making these changes, however, ALM also provides an API that developers can use to do a number of other things: applications can initiate switches, can request notification of switches while they are running, and can get information about user-defined sets of settings. These calls allow developer applications to become "location-aware" or they might be used to create "location-sensing" applications that could initiate switches automatically based on changes in the machine's environment. In ALM 2.0, applications can use API calls to create locations in order to help the user aggregate groups of settings.
Availability
Version 1.0.2 of ALM is available as part of Mac OS 8. Version 2.0 of ALM is scheduled to be part of Mac OS 8.1.
Acknowledgements
Many thanks to the entire ALM team for their tireless efforts in getting ALM into the hands of users and developers. Our collective thanks are also extended to third-party developers who participated in the seed program, without whose feedback and support ALM wouldn't be as useful nor as reliable as it is, and still further thanks for input to the many developers who have emailed, called, and cornered me at developers functions.
Installation
Provided with this SDK are GM versions of ALM 1.0.2 and 2.0 for your use in testing and development. Recall that while ALM 1.0.2 will install on non-PowerBooks, it will not run on them. This distribution does not include a debug build of ALM. ALM 1.0.1 was never released publicly.
What goes where
Although using the installer is recommended, you may be interested in knowing where various pieces are placed, so here is a list of the pieces in the 1.0.2 install:
:System Folder:Control Panels:Location Manager
:System Folder:Control Strip Modules:Location Manager
:System Folder:Extensions:Location Manager Extension
:System Folder:Extensions:Location Manager Guide Addtns
:System Folder:Extensions:Location Manager Modules:Auto-Open Item
:System Folder:Extensions:Location Manager Modules:Default Printer
:System Folder:Extensions:Location Manager Modules:Extensions Mgr
:System Folder:Extensions:Location Manager Modules:File Sharing
:System Folder:Extensions:Location Manager Modules:Network
:System Folder:Extensions:Location Manager Modules:Sound
:System Folder:Extensions:Location Manager Modules:TimeZone
:System Folder:Preferences:Location Manager Prefs:Locations:SampleSimilarly, under ALM 2.0, the chunks are:
:System Folder:Control Panels:Location Manager
:System Folder:Control Strip Modules:Location Manager Controls
:System Folder:Extensions:Location Manager Extension
:System Folder:Extensions:Location Manager Guide Addtns
:System Folder:Extensions:Location Manager Modules:AppleTalk &TCP/IP
:System Folder:Extensions:Location Manager Modules:Auto-Open Item
:System Folder:Extensions:Location Manager Modules:Default Printer
:System Folder:Extensions:Location Manager Modules:Extension Set
:System Folder:Extensions:Location Manager Modules:File Sharing State
:System Folder:Extensions:Location Manager Modules:Internet Access
:System Folder:Extensions:Location Manager Modules:Remote Access
:System Folder:Extensions:Location Manager Modules:Sound Level
:System Folder:Extensions:Location Manager Modules:Time ZoneThese are, of course, localized for different countries.
Using ALM
Once you have installed and restarted, open the control panel and play with some settings. ALM has comprehensive Balloon Help and Apple Guide support for your reference; most users catch on fairly quickly.
If you want to get a feel for how modules work, the SDK provides a sample module, which is compiled to "Generic" and can be used to create a custom module quickly for experimental purposes. We do not, however, recommend that you use this technique to create a "finished" module, particularly since the Generic module has not been heavily tested by any stretch of the imagination!
That said, a lot of what modules do is similar. The Generic module provided with this SDK can be quickly customized with your favorite resource editor to provide a working module for a wide range of software, under the assumption that the software maintains a preference file in its preferences folder. With these instructions, you could create a working module for software that follows the "preference file" model in about 10 minutes.
Before I describe the "customization", however, be aware that, as with most generic solutions, a module derived from Generic without material code changes is not going to be terribly efficient, both in speed and in memory use, and should not be considered the "preferred" means for supporting ALM. I encourage you to plan and design your own module, perhaps by modifying the Generic code itself.
With that said, also be aware that these instructions are "quick & dirty" - very little in the way of explanation is provided. So, make a copy of the Generic module, giving it an appropriate name. The Generic module is set up to store preferences for MicroSoft Word 5, and, although it is in a "working" state, there are some things that need to be tidied up, so if you'll go through that exercise, you'll have a good idea of how to customize the module for your own use.
- Open the Generic module in your favorite resource editor.
- Open 'thng' resource 128. Change the Component Subtype to a unique value. Normally, you will want to use the registered creator for your application, to avoid collisions. we'll use 'MSWD', the creator type of Word. You can also change the Component Manufacturer to whatever you wish. You don't need to change anything else.
- Open 'STR ' resource 10000. Change it to the name of your module, like "MicroSoft Word 5".
- Open 'STR ' resource 10001. Change the string there to a brief description of your module.
- If you are internationalizing your module, you can open 'intl' resource 10000, and change the script, region, language, and default font information for all text your module will return. This is a little bit more involved than I want to get right now, so we'll leave it alone, but it is not difficult to change when you need to.
- Open styled 'TEXT' resource 10000. Following the basic layout shown, type in descriptive information that the user will see in the ALM control panel when they click the Get Info button to find out about your module.
- Open the 'fils' resource 10000. This resource type contains files to consider as part of the setting, one to a resource.In our case, there is only one, it's name is "Word Settings (5)". There is also a companion resource with the same ID, and its type is 'rstr'; it contains an associated flags field telling the module whether the file, if changed, will require a restart or not (the reason these resources are separate has been lost in subsequent design changes, but I digress). If you have multiple files to deal with, you can put them all in separate resource pairs.
- It may seem like the module now has enough information to locate a preference, save and restore it, but there is one problem - what if MicroSoft Word is running? Open the 'apps' resource 10000. This is a list of applications to quit, if they are running, before accessing the preferences. In the case of our example, we'll send a quit event to the application both on getting the settings, and on retrieving them - presumably, we will explain this to the user in our Get Info. I'm sure you can see that it would be preferable to be able to "coexist" with the application so as to share the preferences file, but that does require writing a custom widget, obviously, and will have to be solved differently in every case. And besides, I promised there would be no code in this example.
- As mentioned before, in ALM 2.0, we encourage developers to give at least some thought to supporting EditSetting calls. The Generic module supports EditSetting in a typical though not completely satisfying way: it gives users a chance to launch an application to change their settings, quit it, come back to ALM, and apply the change. Not ideal, but better than sending the user on a snark hunt. Anyway, the Generic module displays 'DLOG' 10000 when its EditSetting method is called. You may wish to edit the text of the message. Also, if you open 'apps' 10001, you can wire applications to the "Open" button of the dialog. Alas, if you have multiple applications to open, this is an all-or-nothing operation: if you want separate buttons for different applications, you've got to roll your own code.
- You could tidy your module up by deleting the TMPL resources and the custom icons (the custom icons are only there so that the icon will show without ALM installed - if it is installed, the Control Panel's bundle will provide the icon).
- You're done! Install your module in the Location Manager Modules folder and restart your machine.
The complexity of writing a module will, of course, vary with the complexity of the software with which the module will interact. As a general rule, however, writing an ALM module is about half the complexity of writing a Control Strip module to perform the same function, but is more complex than buttering toast.
Now that you've seen what you can do just by modifying the compiled Generic module, I hope you'll consider implementing your own module by modifying the Sample code, which is only slightly more involved (you still believe me, don't you?).
Headers & Environments
The ALM headers have been distributed with CodeWarrior Pro 2 and in the Universal Interfaces 3.0.1. This SDK no longer includes its own versions of the headers. Also, if you plan to use a development environment other than the most current MPW or CodeWarrior, you're on your own.
Debugging Modules
In ALM 2.0, once a module's signature is known, you can replace the module as often as you wish; there will be a brief pause while ALM rescans the modules folder, but, other than that, you only need to restart the first time you install your module.
Component Technology
The Component Manager is documented extensively in Inside Macintosh: More Macintosh Toolbox, and much of the discussion there I will either assume or ignore. See also the discussion of the 'thng' resource in the earlier section. Only three items are defined in the Rez header "LocationManager.r". The component type is kALMComponentType ('walk' for those who prefer graphical resource creation). The only other field of interest in the 'thng' resource are the "manufacturer flags", two of which are currently used in ALM:
#define kALMMultiplePerLocation 1 /* bit 0 */
#define kALMDescriptionGetsStale 2 /* bit 1 */Bit kALMMultiplePerLocation indicates that multiple instances of this module can be added to a location; this is how the Auto-Open Item is defined.
Bit kALMDescriptionGetsStale causes ALM to "re-query" a module, to get new descriptions more frequently. Usually, if the settings have not changed, ALM assumes the description has not changed either, but this may not be the case in some modules; for example, the Extensions Manager module is intelligent enough to report that the current extensions set has not changed if the user simply renamed it - the description would refer to the old name if this bit were not set.
All other bits are reserved.
One thing about the Component Manager I will reiterate here is that it is based on "single code resource" technology, and, as such there is one entry point, and the call type is specified by a selector. Negative selectors are defined by the Component Manager, and are used for component "Open" and "Close" calls, among others (see the sample code). Nonnegative selectors correspond to the ALM module API:
kALMGetCurrentSelect
kALMSetCurrentSelect
kALMCompareSettingSelect
kALMEditSettingSelect
kALMDescribeSettingSelect
kALMDescribeErrorSelect
kALMImportExportSelect
kALMGetScriptInfoSelect
kALMGetInfoSelectAlso, in ALM, the convention is to set the "doAutoVersion" bit in the 'thng' resource. Otherwise, duplicate versions of your module may confuse the user, and potentially ALM itself.
You should be aware that, in order to reduce memory footprint, ALM does not keep modules around long, so you can expect modules to be opened and closed frequently - try to keep your module fast. Also, it is generally considered to be bad form to fail in component Open routine, so I suggest you defer most of your initialization to the first "real" call. Remember, if there is a problem, you want to be able to explain it to the user if at all possible, and that requires your component to open successfully. One exception to this rule is if you know the user will never be able to use your module on the given machine; for example, an Infrared module Quadra 840av is probably never going to be useful, and the user probably doesn't expect it to be useful. In that case, it is entirely reasonable to fail in your open routine (though, because of a bug, it's still not recommended if you intend to support ALM 1.0.x).
Handling Global Data
ALM Component Manager-based calls come in two flavors: with global data passed as the first parameter, and without. The ALM header "LocationManager.k.h" provides a means for you do implement ALM calls either all one way, or all the other, using a combination of macros. Using these macros may help you transition your code if ALM were to be implemented using CFM. I recommend using the header in the following manner if you intend to pass global data using the Component Manager:
#define ALM_BASENAME()
#define ALM_GLOBALS() Globals
#include <LocationManager.k.h>You would then define the Globals data type, and expect it as the first parameter to your routines. And if you don't want global data, simply remove the second line, that is:
#define ALM_BASENAME()
#include <LocationManager.k.h>If you prefer to use "real" global data, you're on your own - consult the documentation for your development environment for ideas and strategies. The Generic module fiddles with A5-worlds, but only to initialize QuickDraw in order to (reliably) display its dialog box.
Options to Boot
Some modules require rebooting for the changes to take effect. To assess the impact of such changes, the module API lets ALM know if a reboot is required. If switching to a location involves a module requiring a reboot, ALM offers the user the chance to reboot, shutdown, or continue working after all modules are completed. There a number of options; for the most part, modules will return kALMNoChange (in the event the switch-to setting is the same as the current setting), kALMAvailableNow (in the event no rebooting is required), or kALMExtensions (in the event extensions need to be reloaded; this implies a restart in the current OS). In ALM 2.0, a module can make decisions based on what previous modules have done; for example, if an Extension Set has changed and requires a reboot, any module after the Extension Set could decide to defer its action until after the restart has occurred.
kALMNoChange
kALMAvailableNow
kALMFinderRestart
kALMProcesses
kALMExtensions
kALMWarmBoot
kALMColdBoot
kALMShutdownThe API Itself
This is the moment you've been waiting for - the definitions for each of the nine API calls a module may support (some are optional). In the following, I will assume you've followed the recommendation given above, and defined ALM_BASENAME() to nothing, so that these will be your routines, as prototyped for you in LocationManager.k.h (shown in both non-global and global format). While this summary may be good for reference, to get a feel for the calls I suggest you refer to the sample and explore its functionality.
GetCurrent
pascal ComponentResult GetCurrent (Handle setting);
pascal ComponentResult GetCurrent (Globals globals, Handle setting);When this call is made, the setting will already be allocated (but not the right size), so resize it as necessary; the format of your data is entirely private to you, and ALM simple stores the entire handle, whatever its size. Cast it to your private setting handle type and fill it with information describing whatever preferences your module cares about.
Return an error if the software you need isn't installed, or if the current setting suggests that the software isn't initialized (the Time Zone module, for example, returns an error if the current location has not been set.)
As stated previously, modules should Open even when the software the module works with isn't installed. You can then return an error from GetCurrent to tell the user what's wrong with the system.
Try to keep the setting size to the minimum required to effect a SetCurrent call. For example, the Extension Set module stores little more than the name of the current Extensions Manager Set.
Action/State Module differences
An action module has not concept of "current" settings. Consider the case of the Auto-Open Items module: what is the "current" Auto-Open Items value in the system? Clearly, there is none. Nevertheless, an action module does need to support GetCurrent, but should return a settings value that can be identified as "incomplete" by other calls the module implements. ALM will call EditSetting immediately after GetCurrent with this "incomplete" setting.
SetCurrent
pascal ComponentResult SetCurrent (Handle setting, ALMRebootFlags* flags);
pascal ComponentResult SetCurrent (Globals globals, Handle setting,
ALMRebootFlags* flags);Set the current settings for your module; the setting passed in will be one previously returned to ALM on a GetCurrent call. Indicate via *flags what type of rebooting is necessary, if any. If you cannot complete your SetCurrent call (for example, because you are being called at INIT time), return kALMDeferSwitchErr and ALM will call you again at a "nicer" time.
Under ALM 2.0
The input value of *flags represents the current "escalation level" within a location switch; that is, the highest value set by previous modules. A module might use this information to change its behavior. For example, the Auto-Open Items module will defer opening items if any previous module has signaled a restart requirement (users would have to carefully position their Auto-Open Items setting at the end of their location to experience this benefit). If you cannot (or should not) set the current setting because of the escalation level, you can return kALMRebootFlagsLevelErr.
CompareSetting
pascal ComponentResult CompareSetting (Handle setting1, Handle setting2,
Boolean* equal);
pascal ComponentResult CompareSetting (Globals globals, Handle setting1,
Handle setting2, Boolean* equal);Compare the contents of two settings. ALM uses this to determine if the setting your module is responsible for has changed and requires an update. Updates are currently only done by the user in the ALM Control Panel, but future versions may allow the user to update at other times when a change is detected.
Under ALM 2.0
CompareSetting may be called at switch or restart or shutdown time, if the user has selected the "update settings" option.
EditSetting
pascal ComponentResult EditSetting (Handle setting);
pascal ComponentResult EditSetting (Globals globals, Handle setting);Interact with the user to modify the contents of the setting. Present whatever interface is necessary (launch your application, bring up your control panel, even show a movable modal in the ALM Control Panel's context). If you cause ALM to be put in the background, bring it to the foreground after the editing is done (this implies that you also need to know when editing is "done").
If the user cancels the edit, return userCanceledErr from the function.
This call is currently optional; the Component Manager's "canDo" selector is used to determine if the module supports it or not. Although this call is optional, if you do not implement it, the user will be required to edit "live" settings to make changes, and then use the ALM Control Panel to update the setting; the user may find this confusing. We strongly encourage you to implement this call; future versions of ALM may require it, and your module might be disabled if it does not support EditSetting.
Under ALM 2.0
The user can invoke your EditSetting call by clicking on the "Edit" button with your module hilited. If you don't implement EditSetting, the user gets a nasty "I don't know what to do with this module" dialog, which isn't the best user experience.
Action/State Module differences
Action modules must always implement the EditSetting call because there is no concept of "current settings" for an action module; see additional discussion under GetCurrent above.
DescribeSettings
pascal ComponentResult DescribeSetting (Handle setting, CharsHandle text);
pascal ComponentResult DescribeSetting (Globals globals, Handle setting,
CharsHandle text);Generate text which will be used in the location window to express what the setting represents. The description handle "text" will already be allocated. Resize it to fit the string you plan to return. The length of the text ALM displays is determined by the size of the description handle. The font information returned by GetScriptInfo will be used to display the text.
Under ALM 1.0.x
This routine was called "DescribeSettings". We apologize for our previously inconsistent use of the plural in our API. You can still use DescribeSettings if you define OLDROUTINENAMES to 1 in your compilation.
DescribeError
pascal ComponentResult DescribeError (OSErr lastErr, Str255 errStr);
pascal ComponentResult DescribeError (Globals globals, OSErr lastErr,
Str255 errStr);Generate a string which will be used in the location window (or the switch window) to describe an error. If you return paramErr, a generic routine will be used to describe the error. Make an attempt to describe the errors most likely to be returned by your various other routines. The technique Apple's modules use is to convert generic errors to specific ones; the Extension Set module might translate certain instances of fnfErr (-43) to a private, positive number, which this routine might describe as "The Extensions Manager is not installed."
ImportExport
pascal ComponentResult ImportExport (Boolean import, Handle setting,
SInt16 resRefNum);
pascal ComponentResult ImportExport (Globals globals, Boolean import,
Handle setting, SInt16 resRefNum);This call is optional, and many modules do not need to implement it. However, consider the case of the Extension Set module: it stores the name of the current extensions set, but what does that mean on another machine? ImportExport to the rescue.
There are many strategies that you can use to manage the "duality" of settings that normally only need to store a little information when used on the same machine, but need to capture a lot of information if the setting is going to be moved to another machine.
If a module supports this call, it may be useful to define a field in the setting to indicate whether it is in exported or local form, assuming the data is all to be stored in the handle still.
If it is not convenient to have two forms of setting handle, you can call UseResFile (resRefNum), to set the resource fork to the current import/export file, and add additional data as resources (or remove them, in the import case). To avoid resource collisions, you use your module's component signature as a reserved resource type. Special strategies may be necessary if you add resources and your module allows itself to be added to a location multiple times.
GetScriptInfo
pascal ComponentResult GetScriptInfo (ALMScriptManagerInfo* info);
pascal ComponentResult GetScriptInfo (Globals globals,
ALMScriptManagerInfo* info);This call gets information from the module that will allow ALM to display the various strings the module returns in the correctly localized script and region. Return the fields in accordance with how your module has been localized.
#pragma options align=mac68k
typedef struct {
SInt16 version;
SInt16 scriptCode;
SInt16 regionCode;
SInt16 langCode;
SInt16 fontNum;
SInt16 fontSize;
} ALMScriptMgrInfo;
#pragma options align=resetAlthough this call is optional, it is recommended that you implement it, along the style provided in the sample code: the headers even provide a conveniently localizable alternate form (the sample shows how to use it). If you do not implement it, ALM will assume the current environment, something like this:
version = kALMScriptInfoVersion;
scriptCode = smSystemScript;
regionCode = GetScriptManagerVariable (smRegionCode);
langCode = GetScriptVariable (smSystemScript,
smScriptLang);
fontNum = GetScriptVariable (smSystemScript,
smScriptAppFond);
fontSize = GetScriptVariable (smSystemScript,
smScriptAppFondSize);GetInfo
pascal ComponentResult GetInfo (CharsHandle* text, STHandle* style,
ModalFilterUPP filter);
pascal ComponentResult GetInfo (Globals globals, CharsHandle* text,
STHandle* style, ModalFilterUPP filter);This call is made when the user clicks the "Get Info" button in the location editing window.
It can return a text handle (and an optional style handle), or it can do its own thing, such as launching an external help system or displaying its own dialog. The text, if returned, will be displayed in a scrolling window.
Although ALM is, by itself, a fairly comprehensive software suite, it was recognized early in the design that if the framework were exposed, third-party developers could exploit some of ALM's internal workings to create customized solutions that we hadn't thought of. Also, during the development of modules, some common tasks began to emerge that we think other module developers would prefer not to reimplement.
In this SDK, you will find the header "LocationManager.h", interface "LocationManager.p" and even "LocationManager.a", if you are so inclined. If your project is PowerPC-based, you will need to link with the stub library LocationManagerLib. Weak linking is recommended; compare one of the seven exported routines against kUnresolvedCFragSymbolAddress (before calling any of them) to see if ALM is installed. Note that although the stub library does contain CFM-68K stubs, ALM 1.0 as currently distributed, including with this SDK, does not contain the corresponding CFM-68K implementation. For this reason, I recommend classic 68K development for now.
AppleScript
Although this is mentioned in the user's Read Me, if you prefer to speak AppleScript, it is possible to change the current location thusly:
tell
application "Finder"
open file ((control panels folderas
string) & "Location Manager")
tell
application "Location Manager"
set
current location
to
location myLocationName
end
tell
end
tell
If that fails to amuse you, consider the script to return the name of the current location by:
tell
application "Finder"
open file ((control panels folderas
string) & "Location Manager")
tell
application "Location Manager"
set
currLoc
to
current location
set
curLocName
to
name of currLoc
end
tell
end
tell
curLocName
If you see specific functionality that you think should be scripted, please send your feedback to DevSupport.
Determining what ALM features are available
As with most things Mac, you should use gestalt to check for ALM (you do not need to do this in a module, since modules will only be called if ALM is around anyway, so checking would be somewhat redundant). Two gestalt selectors are defined: gestaltALMVers which returns the version of ALM installed in NumVersion format for your interest (get the pieces using the union NumVersionVariant.whole), and gestaltALMAttr which returns the attributes of ALM; you should test bit gestaltALMPresent to see if ALM is installed; there are other bits for version-dependant features.
Managing Locations
Four routines are defined to let you examine the locations a user has defined.
pascal OSErr
ALMGetCurrentLocation (SInt16* index, ALMToken* token, ALMLocationName name);Gets three values describing the current location. Pass NULL for any value you don't want. Index is zero-based; in ALM 1.0, there can be at most 16 locations, so *index will never be more than 15, but this may change in future releases. If there is no current location, the *index will be returned as kALMNoLocationIndex, *token will be kALMNoLocationToken, and the name will be "None (off)", as localized. An ALMToken is a private ALM data structure reference.
pascal OSErr
*
ALMGetIndLocation (SInt16 index, ALMTokentoken, ALMLocationName name);
Use this call to index through the defined locations, returning their tokens and names. Note that you can pass kALMNoLocationIndex to get the localized version of the "None (off)" state. This function will return paramErr if the index is out of range. Again, pass NULL for any value you don't want returned.
pascal OSErr
* locationCount
ALMCountLocations (SInt16);
Returns the number of locations defined.
pascal OSErr
ALMSwitchToLocation (ALMToken newLocation, ALMSwitchActionFlags switchFlags);Initiates a location switch. The switchFlags parameter is a set of flags that control switching options. Use kALMDefaultSwitchFlags for a normal switch or kALMDontShowStatusWindow for a "quiet" switch that does not put up the dialog box (not recommended).
If you are not sure about the environment in which you will be switching (for example, you are inside a background process, or inside a process that doesn't respond well to having a dialog suddenly popup inside its context), add the flag kALMSignalViaAE and the switch will be deferred until the Finder (or the Location Manager Control Panel) is the current process. The flag is so-named because the context is guaranteed by sending an AppleEvent since the Finder won't process an AppleEvent while it is not the current process!
Under ALM 1.0.x, do not make this call at INIT time!
Creating/Naming/Getting Locations
These features are only available if the gestaltALMAttr bit number gestaltALMHasSFLocation is set (ALM 2.0). You may notice that these calls resemble the well-known Standard File package, and that is deserved - the filter and yourDataPtr parameters behave exactly as in the Standard File equivalents to these calls; to see them in action, you need only create or manipulate locations using the control panel.
pascal OSErr
ALMPutLocation (ConstStr255Param prompt, ALMLocationName name, SInt16 numTypes,
ConstALMModuleTypeListPtr typeList, ModalFilterYDUPP filter,
void* yourDataPtr);
The prompt appears above the dialog that is presented; the name is the default name on input (which the user may modify - the changed name is returned). The typeList is an array of module signatures, with numTypes indicating how many items are in the array, or, you may pass NULL for typeList, and kALMAddAllOff for numTypes to create an "empty" location. To create a location which uses all installed modules to capture the current system settings, pass kALMAddAllOnSimple for numTypes (action modules are not added to the location with this value). Returns userCanceledErr if the user dismisses the dialog.
pascal OSErr
ALMMergeLocation (ConstStr255Param prompt, ALMLocationName name, SInt16 numTypes,
ConstALMModuleTypeListPtr typeList, ModalFilterYDUPP filter,
void* yourDataPtr);
This behaves almost identically to ALMPutLocation, except that the user must choose an existing location. Any existing values in the location are replaced.
pascal OSErr
ALMGetLocation (ConstStr255Param prompt, ALMLocationName name,
ModalFilterYDUPP filter, void* yourDataPtr);
Provided for completeness; prompts the user to choose a location, returning the name of that location.
Detecting Switches & Changes in the Locations list
ALM sends events to all AE-aware applications currently running whenever a switch occurs. The event has an AEEventClass of kAECoreSuite and an AEEventID of kAESystemConfigNotice. If event contains a parameter keyed under kAELocationChangedNoticeKey, treat the data as a 32-bit quantity representing an ALMToken of the location to which the user just switched.
Under ALM 2.0, if bit number gestaltALMHasRescanNotifiers is returned on from gestaltALMAttr, you will also receive (with the same class and ID) events when the user renames, creates, or deletes locations, but there will be a parameter keyed under kAELocationRescanNoticeKey instead. The value of the key is also a 32-bit ALMToken, of the current location.
As always, any events you receive that do not have parameters you recognize should be ignored.
If you are not an application (no, not you personally, but surely you view yourself as your code, right?), you can use the following routines to register a callback routine for notification:
pascal OSErr
*
ALMRegisterNotifyProc (ALMNotificationUPP notificationProc,
const ProcessSerialNumberwhichPSN);
pascal OSErr
*
ALMRemoveNotifyProc (ALMNotificationUPP notificationProc,
const ProcessSerialNumberwhichPSN);
You create an ALMNotificationUPP using the conventional myRoutineUPP = NewALMNotificationProc (MyALMNotificationRoutine), and dispose of it with DisposeRoutineDescriptor (myRoutineUPP). Your routine should conform to this interface:
pascal void
*
MyALMNotificationRoutine (AppleEventtheEvent);
Extract kAELocationNotice from theEvent in the same fashion as you would in an AEHandler.
This just in
It has been discovered that AppleEvents are not always sent; if you are having problems in you application with receive AppleEvents from ALM, be aware that you can still the routines ALMRegisterNotifyProc and ALMRemoveNotifyProc from an application.
Module Import Collisions
As discussed earlier, where preferences supported by a module keep track of named configurations, such as under Open Transport or Extensions Manager, the module should only keep track of the name of that configuration, until it needs to do an export. The problem becomes, then, that an Importing user might have a setting with the same name that is not the same setting. Obviously, it would not be good to blindly overwrite the existing setting. So, ALM provides the above call so that a module may prompt the user, without going through the general hassle of setting up a user interface within a code resource, for a new name (or to replace the old name).
pascal OSErr
ALMConfirmName (ConstStr255Param msg, Str255 configName,
SInt16* choice, ModalFilterUPP filter);
Typically, the module developer will call this routine with msg as a message indicating the conflict occurred. If a ^0 is embedded within the string, it will be replaced with the configName parameter for presentation to the user in the dialog (for example, if msg is "The config named "^0" exists." and configName is "MyConfig", the user sees a dialog with "The config named "MyConfig" exists."). If the user chooses to rename the config, the new name is returned in configName, and *choice is set to ALMConfirmRenameConfig; you should then check to see that the user hasn't collided with yet another config, possibly making this call again. If the user chooses to replace the config, you will get *choice of ALMConfirmReplaceConfig. The only other possibility is that the user cancels the whole operation, in which case the function returns userCanceledErr and *choice is not defined.
The final parameter, filter, is a standard dialog filter that you can use to filter events; set it to NULL otherwise. In ALM 1.0.x, the filter function is only called while the second dialog is up. This is a bug; future versions will call your filter function during both dialogs' tenure. If the refcon of the dialog is zero, you can presume you are being called from ALM 1.0.x. Under ALM 2.0, the duplicate dialog has a refcon of kALMDuplicateDialogRefCon, while the rename dialog has a refcon of kALMRenameDialogRefCon.
Additionally, to assist you in writing your filter, the dialog item numbers are provided for your use; see the interface file.
Errors
Listed here for completeness; most of these are unremarkable, although recall that a module that cannot switch at startup time should return kALMDeferSwitchErr if it cannot handle a SetCurrent call at boot time. Generally, modules should only return errors that can be translated into informative messages to users via the DescribeError call.
kALMInternalErr = -30049
kALMLocationNotFoundErr = -30048
kALMNoSuchModuleErr = -30047
kALMModuleCommunicationErr = -30046
kALMDuplicateModuleErr = -30045
kALMInstallationErr = -30044
kALMDeferSwitchErr = -30043
kALMRebootFlagsLevelErr = -30042
Shared Library Model
ALM has a long genesis, and currently uses the Component Manager to implement modules. A future version of ALM is likely to support CFM, but Component Manager modules will probably continue to be supported simultaneously for a period of time to protect users' investment in your software.
Beyond 2.0
As this is written, ALM 2.0 is just out the door, and, although we already have a "wish list" for future versions, it would be premature to comment on the possibilities right now.
Web
We try to maintain a website on DevWorld.
Reporting Bugs and Requests
If you find a problem or have a suggestion to make with respect to ALM or the SDK, please submit your report to DevSupport.
Reference Material
"Preferential Treatment Under Apple Location Manager", an article that covers the ALM API in depth, including 2.0-specific features, in MacTech magazine, September 1997.
Techniques for Writing and Debugging Components, develop 12.
Component Manager, Inside Macintosh: More Macintosh Toolbox.